home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 005 / tsrcom16.arc / RELEASE.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1986-03-20  |  14.2 KB  |  419 lines

  1. {**************************************************************************
  2. *   Releases memory above the last MARK call made.                        *
  3. *   Copyright (c) 1986 Kim Kokkonen, TurboPower Software.                 *
  4. *   Released to the public domain for personal, non-commercial use only.  *
  5. ***************************************************************************
  6. *   Version 1.0 2/8/86                                                    *
  7. *     original public release.                                            *
  8. *     (thanks to Neil Rubenking for an outline of the method used)        *
  9. *   Version 1.1 2/11/86                                                   *
  10. *     fixed problem with processes which deallocate their environment.    *
  11. *   Version 1.2 2/13/86                                                   *
  12. *     fixed another problem with processes which deallocate environment.  *
  13. *   Version 1.3 2/15/86                                                   *
  14. *     added support for "named" marks.                                    *
  15. *   Version 1.4 2/23/86                                                   *
  16. *     added support for releasing programs which use Expanded Memory.     *
  17. *   Version 1.5 2/28/86                                                   *
  18. *     added more bulletproof method of finding first allocation block.    *
  19. *   Version 1.6 3/20/86                                                   *
  20. *     restore all FF interrupts.                                          *
  21. *     restore the termination address to the local process.               *
  22. *     reduce number of EMS blocks to 32.                                  *
  23. *     fix bug in number of EMS handles in EMS release step.               *
  24. *     restore a mysterious address in the PSP which allows RELEASE of a   *
  25. *       COMMAND shell.                                                    *
  26. ***************************************************************************
  27. *   telephone: 408-378-3672, CompuServe: 72457,2131.                      *
  28. *   requires Turbo version 3 to compile.                                  *
  29. *   Compile with mAx dynamic memory = FFFF.                               *
  30. ***************************************************************************}
  31.  
  32. {$P128}
  33.  
  34. PROGRAM ReleaseTSR;
  35.   {-release system memory above the last mark call}
  36.   {-release expanded memory blocks allocated since the last mark call}
  37.  
  38. CONST
  39.   Version = '1.6';
  40.   MaxBlocks = 128;            {max number of DOS allocation blocks supported}
  41.   MaxHandles = 32;            {max number of EMS allocation blocks supported}
  42.   EMSinterrupt = $67;         {the vector used by the expanded memory manager}
  43.  
  44.   markID = 'MARK PARAMETER BLOCK FOLLOWS'; {marking string for TSR MARK}
  45.  
  46.   {offsets into resident copy of MARK.COM for data storage}
  47.   markOffset = $103;          {where markID is found in TSR}
  48.   vectorOffset = $120;        {where vector table is stored}
  49.   EMScntOffset = $520;        {where count of EMS active pages is stored}
  50.   EMSmapOffset = $522;        {where the page map is stored}
  51.  
  52. TYPE
  53.   registers =
  54.   RECORD
  55.     CASE Integer OF
  56.       1 : (ax, bx, cx, dx, bp, si, di, ds, es, flags : Integer);
  57.       2 : (al, ah, bl, bh, cl, ch, dl, dh : Byte);
  58.   END;
  59.  
  60.   HandlePageRecord =
  61.   RECORD
  62.     handle : Integer;
  63.     numpages : Integer;
  64.   END;
  65.  
  66.   PageArray = ARRAY[1..MaxHandles] OF HandlePageRecord;
  67.   PageArrayPtr = ^PageArray;
  68.  
  69.   Block =
  70.   RECORD                      {store info about each memory block}
  71.     mcb : Integer;
  72.     psp : Integer;
  73.     releaseIt : Boolean;
  74.   END;
  75.  
  76.   BlockType = 0..MaxBlocks;
  77.   BlockArray = ARRAY[BlockType] OF Block;
  78.   AllStrings = STRING[255];
  79.   HexString = STRING[4];
  80.  
  81. VAR
  82.   Blocks : BlockArray;
  83.   bottomBlock, blockNum : BlockType;
  84.   markName : AllStrings;
  85.   Regs : registers;
  86.   StoredHandles, EMShandles : Integer;
  87.   Map, StoredMap : PageArrayPtr;
  88.  
  89.   PROCEDURE FindTheBlocks;
  90.     {-scan memory for the allocated memory blocks}
  91.   CONST
  92.     MidBlockID = $4D;         {byte DOS uses to identify part of MCB chain}
  93.     EndBlockID = $5A;         {byte DOS uses to identify last block of MCB chain}
  94.   VAR
  95.     mcbSeg : Integer;         {segment address of current MCB}
  96.     nextSeg : Integer;        {computed segment address for the next MCB}
  97.     gotFirst : Boolean;       {true after first MCB is found}
  98.     gotLast : Boolean;        {true after last MCB is found}
  99.     idbyte : Byte;            {byte that DOS uses to identify an MCB}
  100.  
  101.     FUNCTION GetStartMCB : Integer;
  102.       {-return the first MCB segment}
  103.     BEGIN
  104.       Regs.ah := $52;
  105.       MsDos(Regs);
  106.       GetStartMCB := MemW[Regs.es:(Regs.bx-2)];
  107.     END {getstartmcb} ;
  108.  
  109.     PROCEDURE StoreTheBlock(VAR mcbSeg, nextSeg : Integer;
  110.                             VAR gotFirst, gotLast : Boolean);
  111.       {-store information regarding the memory block}
  112.     VAR
  113.       nextID : Byte;
  114.       pspAdd : Integer;       {segment address of the current PSP}
  115.       mcbLen : Integer;       {size of the current memory block in paragraphs}
  116.  
  117.     BEGIN
  118.  
  119.       mcbLen := MemW[mcbSeg:3]; {size of the MCB in paragraphs}
  120.       nextSeg := Succ(mcbSeg+mcbLen); {where the next MCB should be}
  121.       pspAdd := MemW[mcbSeg:1]; {address of program segment prefix for MCB}
  122.       nextID := Mem[nextSeg:0];
  123.  
  124.       IF gotLast OR (nextID = EndBlockID) OR (nextID = MidBlockID) THEN BEGIN
  125.         blockNum := Succ(blockNum);
  126.         gotFirst := True;
  127.         WITH Blocks[blockNum] DO BEGIN
  128.           mcb := mcbSeg;
  129.           psp := pspAdd;
  130.         END;
  131.       END;
  132.  
  133.     END {storetheblock} ;
  134.  
  135.   BEGIN
  136.  
  137.     {initialize}
  138.     mcbSeg := GetStartMCB;
  139.     gotFirst := False;
  140.     gotLast := False;
  141.     blockNum := 0;
  142.  
  143.     {scan all memory until the last block is found}
  144.     REPEAT
  145.       idbyte := Mem[mcbSeg:0];
  146.       IF idbyte = MidBlockID THEN BEGIN
  147.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  148.         IF gotFirst THEN mcbSeg := nextSeg ELSE mcbSeg := Succ(mcbSeg);
  149.       END ELSE IF gotFirst AND (idbyte = EndBlockID) THEN BEGIN
  150.         gotLast := True;
  151.         StoreTheBlock(mcbSeg, nextSeg, gotFirst, gotLast);
  152.       END ELSE BEGIN
  153.         {start block was invalid}
  154.         WriteLn('corrupted allocation chain or program error');
  155.         Halt(1);
  156.       END;
  157.     UNTIL gotLast;
  158.  
  159.   END {findtheblocks} ;
  160.  
  161.   FUNCTION FindMark(idString, markName : AllStrings;
  162.                     idOffset : Integer) : Integer;
  163.     {-find the last memory block matching idstring at offset idoffset}
  164.   VAR
  165.     b : BlockType;
  166.     FoundIt : Boolean;
  167.  
  168.     FUNCTION MatchString(segment : Integer;
  169.                          idString, markName : AllStrings;
  170.                          idOffset : Integer) : Boolean;
  171.       {-return true if idstring is found at segment:idoffset}
  172.     VAR
  173.       tString : AllStrings;
  174.       len : Byte;
  175.       FoundIt : Boolean;
  176.  
  177.       FUNCTION StUpcase(s : AllStrings) : AllStrings;
  178.         {-return the uppercase string}
  179.       VAR
  180.         i : Byte;
  181.       BEGIN
  182.         FOR i := 1 TO Length(s) DO s[i] := UpCase(s[i]);
  183.         StUpcase := s;
  184.       END {stupcase} ;
  185.  
  186.     BEGIN
  187.       len := Length(idString);
  188.       tString[0] := Chr(len);
  189.       Move(Mem[segment:idOffset], tString[1], len);
  190.       FoundIt := (tString = idString);
  191.       IF FoundIt AND (markName <> '') THEN BEGIN
  192.         {check the mark name stored in the PSP of the mark block}
  193.         Move(Mem[segment:$80], tString[0], 128);
  194.         WHILE (tString[1] = ' ') OR (tString[1] = ^I) DO Delete(tString, 1, 1);
  195.         FoundIt := (StUpcase(tString) = StUpcase(markName));
  196.       END;
  197.       MatchString := FoundIt;
  198.     END {matchstring} ;
  199.  
  200.   BEGIN
  201.     {scan from the last block-1 down to find the last MARK TSR}
  202.     b := Pred(blockNum);
  203.     REPEAT
  204.       FoundIt := MatchString(Blocks[b].psp, idString, markName, idOffset);
  205.       IF NOT(FoundIt) THEN b := Pred(b);
  206.     UNTIL (b < 1) OR FoundIt;
  207.     IF NOT(FoundIt) THEN BEGIN
  208.       WriteLn('No matching memory marker found. Mark memory by running MARK.COM.');
  209.       Halt(1);
  210.     END;
  211.     FindMark := b;
  212.   END {findmark} ;
  213.  
  214.   PROCEDURE CopyVectors(bottomBlock : BlockType; vectorOffset : Integer);
  215.     {-put interrupt vectors back into table}
  216.   BEGIN
  217.     {interrupts off}
  218.     INLINE($FA);
  219.     {replace vectors}
  220.     Move(Mem[Blocks[bottomBlock].psp:vectorOffset], Mem[0:0], 1024);
  221.     {move the old termination/break/error addresses into this program}
  222.     Move(Mem[0:$88], Mem[CSeg:$0A], 12);
  223.     {move into a mysterious address used by the DOS EXIT command to remove a shell}
  224.     Move(Mem[CSeg:$0C], Mem[CSeg:$16], 2);
  225.     {interrupts on}
  226.     INLINE($FB);
  227.   END {copyvectors} ;
  228.  
  229.   FUNCTION Hex(i : Integer) : HexString;
  230.     {-return hex representation of integer}
  231.   CONST
  232.     hc : ARRAY[0..15] OF Char = '0123456789ABCDEF';
  233.   VAR
  234.     l, h : Byte;
  235.   BEGIN
  236.     l := Lo(i); h := Hi(i);
  237.     Hex := hc[h SHR 4]+hc[h AND $F]+hc[l SHR 4]+hc[l AND $F];
  238.   END {hex} ;
  239.  
  240.   PROCEDURE MarkBlocks(bottomBlock : BlockType);
  241.     {-mark those blocks to be released}
  242.     {complicated by TSRs which deallocate their environment}
  243.   VAR
  244.     b : BlockType;
  245.     BottomPSP : Real;
  246.  
  247.     FUNCTION Cardinal(i : Integer) : Real;
  248.       {-return a real 0..65535}
  249.     BEGIN
  250.       Cardinal := 256.0*Hi(i)+Lo(i);
  251.     END {cardinal} ;
  252.  
  253.   BEGIN
  254.     BottomPSP := Cardinal(Blocks[bottomBlock].psp);
  255.     FOR b := 1 TO blockNum DO WITH Blocks[b] DO BEGIN
  256.       releaseIt := False;
  257.       IF (b < Pred(bottomBlock)) AND (Cardinal(psp) >= BottomPSP) THEN
  258.         WriteLn('WARNING: trapped memory block at PSP ', Hex(psp), ' will not be released')
  259.       ELSE IF (Cardinal(psp) >= BottomPSP) AND (psp <> CSeg) THEN
  260.         releaseIt := True;
  261.     END;
  262.   END {markblocks} ;
  263.  
  264.   PROCEDURE ReleaseMem;
  265.     {release DOS memory marked for release}
  266.   VAR
  267.     b : BlockType;
  268.   BEGIN
  269.     WITH Regs DO
  270.       FOR b := 1 TO blockNum DO WITH Blocks[b] DO
  271.         IF releaseIt THEN BEGIN
  272.           ah := $49;
  273.           {the block is always 1 paragraph above the MCB}
  274.           es := Succ(mcb);
  275.           MsDos(Regs);
  276.           IF Odd(flags) THEN BEGIN
  277.             WriteLn('Could not release block at segment ', Hex(es));
  278.             WriteLn('Memory is now a mess... Please reboot');
  279.             Halt(1);
  280.           END;
  281.         END;
  282.   END {releasemem} ;
  283.  
  284.   FUNCTION EMSpresent : Boolean;
  285.     {-return true if EMS memory manager is present}
  286.   VAR
  287.     f : FILE;
  288.   BEGIN
  289.     {"file handle" defined by the expanded memory manager at installation}
  290.     Assign(f, 'EMMXXXX0');
  291.     {$I-} Reset(f) {$I+} ;
  292.     EMSpresent := (IOResult = 0);
  293.     Close(f);
  294.   END {EMSpresent} ;
  295.  
  296.   FUNCTION EMShandlesActive : Integer;
  297.     {-return the number of active EMS handles}
  298.   BEGIN
  299.     Regs.ah := $4B;
  300.     Intr(EMSinterrupt, Regs);
  301.     IF Regs.ah <> 0 THEN BEGIN
  302.       WriteLn('EMS device not responding');
  303.       EMShandlesActive := 0;
  304.       Exit;
  305.     END;
  306.     EMShandlesActive := Regs.bx;
  307.   END {EMShandlesActive} ;
  308.  
  309.   FUNCTION GetHandles(bottomBlock : BlockType; EMScntOffset : Integer) : Integer;
  310.     {-return the number of handles stored by mark}
  311.   VAR
  312.     gh : Integer;
  313.   BEGIN
  314.     Move(Mem[Blocks[bottomBlock].psp:EMScntOffset], gh, 2);
  315.     GetHandles := gh;
  316.   END {gethandles} ;
  317.  
  318.   PROCEDURE EMSpageMap(VAR PageMap : PageArray);
  319.     {-return an array of the allocated memory blocks}
  320.   BEGIN
  321.     Regs.ah := $4D;
  322.     Regs.es := Seg(PageMap);
  323.     Regs.di := Ofs(PageMap);
  324.     Regs.bx := 0;
  325.     Intr(EMSinterrupt, Regs);
  326.     IF Regs.ah <> 0 THEN
  327.       WriteLn('EMS device not responding');
  328.   END {EMSpageMap} ;
  329.  
  330.   PROCEDURE ReleaseEMSblocks(VAR oldmap, newmap : PageArray);
  331.     {-release those EMS blocks allocated since MARK was installed}
  332.   VAR
  333.     o, n, nhandle : Integer;
  334.  
  335.     PROCEDURE EMSdeallocate(EMShandle : Integer);
  336.       {-release the allocated expanded memory}
  337.     BEGIN
  338.       Regs.ah := $45;
  339.       Regs.dx := EMShandle;
  340.       Intr(EMSinterrupt, Regs);
  341.       IF Regs.ah <> 0 THEN BEGIN
  342.         WriteLn('Program error or EMS device not responding');
  343.         WriteLn('EMS memory is now a mess... Please reboot');
  344.         Halt;
  345.       END;
  346.     END;                      {EMSdeallocate}
  347.  
  348.   BEGIN
  349.     FOR n := 1 TO EMShandles DO BEGIN
  350.       {scan all current handles}
  351.       nhandle := newmap[n].handle;
  352.       IF StoredHandles > 0 THEN BEGIN
  353.         {see if current handle matches one stored by MARK}
  354.         o := 1;
  355.         WHILE (oldmap[o].handle <> nhandle) AND (o <= StoredHandles) DO
  356.           o := Succ(o);
  357.         {if not, deallocate the current handle}
  358.         IF (o > StoredHandles) THEN
  359.           EMSdeallocate(nhandle);
  360.       END ELSE
  361.         {no handles stored by MARK, deallocate all current handles}
  362.         EMSdeallocate(nhandle);
  363.     END;
  364.   END {releaseEMSblocks} ;
  365.  
  366. BEGIN
  367.  
  368.   WriteLn;
  369.  
  370.   {see if a particular mark is named}
  371.   IF ParamCount > 0 THEN
  372.     markName := ParamStr(1)
  373.   ELSE
  374.     markName := '';
  375.  
  376.   {get all allocated memory blocks in normal memory}
  377.   FindTheBlocks;
  378.  
  379.   {find the last one marked with the MARK idstring, and MarkName if specified}
  380.   bottomBlock := FindMark(markID, markName, markOffset);
  381.  
  382.   {copy the vector table from the MARK resident}
  383.   CopyVectors(bottomBlock, vectorOffset);
  384.  
  385.   {mark those blocks to be released}
  386.   MarkBlocks(bottomBlock);
  387.  
  388.   {release normal memory marked for release}
  389.   ReleaseMem;
  390.  
  391.   {see if expanded memory card is installed}
  392.   IF EMSpresent THEN BEGIN
  393.     {see how many EMS handles are currently active}
  394.     EMShandles := EMShandlesActive;
  395.     IF EMShandles > MaxHandles THEN
  396.       WriteLn('EMS process count exceeds capacity of RELEASE')
  397.     ELSE IF EMShandles <> 0 THEN BEGIN
  398.       {see how many handles were active when MARK was installed}
  399.       StoredHandles := GetHandles(bottomBlock, EMScntOffset);
  400.       {get the existing EMS page map}
  401.       GetMem(Map, 4*EMShandles);
  402.       EMSpageMap(Map^);
  403.       {get the stored page map}
  404.       StoredMap := Ptr(Blocks[bottomBlock].psp, EMSmapOffset);
  405.       {compare the two maps and deallocate those not in the stored map}
  406.       ReleaseEMSblocks(StoredMap^, Map^);
  407.     END;
  408.   END;
  409.  
  410.   {DOS will release this program's memory when it exits}
  411.   {write success message}
  412.   Write('RELEASE ', Version, ' - Memory released above last MARK ');
  413.   IF markName <> '' THEN
  414.     WriteLn('(', markName, ')')
  415.   ELSE
  416.     WriteLn;
  417.  
  418. END.
  419.